#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMessageBox>
#include <QStringList>
#include <QTextStream>
#include <algorithm>
#include <QDebug>

class Variable {
public:
    QChar id;
    int exp;
    Variable() {}
    Variable(QChar c, int i) : id(c), exp(i) {}  // 存储字母和指数
    bool operator== (const Variable& v) const {
        return id == v.id && exp == v.exp;
    }
    bool operator< (const Variable& v) const {
        return id < v.id;  // 字母是字典序排序
    }
};

// 用于表示多项式中的项，包含系数和一个包含变量的向量（不包括前面的符号，符号在多项式类里面规定）
class Term {
public:
    int coeff;
    QVector<Variable> vars;
    bool operator== (const Term&) const;
    bool operator!= (const Term& term) const {
        return !(*this == term);
    }
    bool operator< (const Term&) const;
    bool operator> (const Term& term) const {  // required by <list>
        return *this != term && (*this < term);
    }
    int min(int n, int m) const {
        return (n < m) ? n : m;
    }
};

// 多项式类
class Polynomial {
public:
    QVector<Term> terms;  // 多项式本质是一个单项式的列表
    Polynomial operator+ (const Polynomial&);
    QString toString() const;
    // 函数：打印错误信息并退出程序
    void error(const QString& s) {  // 错误信息是传进入的不可变的字符串，因此形参要加const
        QMessageBox::critical(nullptr, "Error", s);
    }
    // 输入流的重载：读取一个多项式   输入流中规定：输入的合法格式类似为 2x + 3y - 6y2;
    // 即非数字的字符必须是 “ +、-、; 空格 ”   不能出现别的字符，多项式必须以 ; 结尾
    friend QTextStream& operator>> (QTextStream& in, Polynomial& polyn) {
        QChar ch, coeffUsed, id;  // sign是多项式中每项前面的符号
        int exp, sign;
        bool inlegal = false;
        Term term;
        in >> ch;
        while (true) {  // 一次while循环处理一个单项式term
            while (ch == ' '){
                in >> ch;
            }
            coeffUsed = 0;
            // 非字符输入，且不是 + - ; 这三种字符的任意一种
            if (!ch.isLetterOrNumber() && ch != ';' && ch != '-' && ch != '+'){
                // 打印错误信息：输入非法字符
                polyn.error("出现非法字符，请重新输入! ");
                break;
            }
            sign = 1;  // 默认符号为正，除非出现-，才把sign取反
            while (ch == '-' || ch == '+') {  // 最开始会拿到符号
                if (ch == '-')
                    sign *= -1;
                QString nextChar = in.read(1);
                if (nextChar.isEmpty()){
                    polyn.error("'+or-'后面缺少term，请重新输入! ");
                    inlegal = true;
                    break;
                }
                ch = nextChar.at(0);
                while (ch.isSpace())
                    in >> ch;  // 如果是空白字符，那就读取到
            }
            if (ch.isDigit()) {   // 读取到系数
                in.seek(in.pos() - 1);  // 回退一个字符
                in >> term.coeff;   // 再次读取，放到coeff中
                ch = in.read(1).at(0);
                term.coeff *= sign;  // 系数加上+-号
                coeffUsed = 1;
            }
            else term.coeff = sign;  // 没有系数，默认系数为1，需加上+-号

            int i;
            for (i = 0; ch.isLetterOrNumber(); i++) {  // 处理这个term: 直到它不是字母或数字为止

                id = ch.toLatin1();
                qDebug() << "id" << id;
                // 读取下一个字符
                QString nextChar = in.read(1);
                qDebug() << nextChar;


                // 测试' '是否为空字符串？
                // QString sc = "";
                // qDebug() << "empty? " << sc.isEmpty();

                // 检查是否成功读取到字符
                if (nextChar.isEmpty()) {
                    // 处理无法读取字符的情况
                    polyn.error("无法读取字符';'，请重新输入! ");
                    inlegal = true;
                    break;
                }

                ch = nextChar.at(0);

                qDebug() << "执行了吗 ？ ";
                if (ch.isDigit()) {         // 读到指数
                    in.seek(in.pos() - 1);  // 回退一个字符
                    in >> exp;              // 输入到exp指数和ch字符里
                    ch = in.read(1).at(0);
                }
                else exp = 1;               // 没有指数，默认为1
                term.vars.push_back(Variable(id, exp));  // 将读取到的一个变量和指数放入Variable数组里
            }
            if (inlegal)  break;

            polyn.terms.push_back(term);  // 将这个构成单项式的列表作为元素放入多项式数组里
            term.vars.clear();
            while (ch.isSpace())
                in >> ch;
            if (ch == ';')
                if (coeffUsed == 1 || i > 0)  // 多项式结束标志
                    break;
                else {
                    polyn.error("缺失某个单项式? ");  // 丢失单项式  e.g., 2x - ; or just ';'
                    break;
                }
            else if (ch != '-' && ch != '+'){          // 丢失加减字符或输入字符错误  e.g., 2x  4y;
                qDebug() << "非法!!!" << ch;
                polyn.error("输入非法 (如：丢失加减字符) ");
                break;
            }
        }
        // 对整个多项式的单项式列表进行排序
        for (int i = 0; i < polyn.terms.size(); i++)
            if (polyn.terms[i].vars.size() > 1)
                std::sort(polyn.terms[i].vars.begin(), polyn.terms[i].vars.end());
        return in;
    }

};

// 如果所有的变量相同，并且相应的变量都被升到相同的幂次，那么两个项是相等的；
// 由于第一个单元格存储了项的系数，因此在比较中排除了该单元格；

// 两个单项式的比较
bool Term::operator== (const Term& term) const {
    int i;  // 相等的字符数量
    // 循环体中没有写什么，就是在较小的长度中进行比较，如果相等就i++
    for (i = 0; i < std::min(vars.size(), term.vars.size()) && vars[i] == term.vars[i]; i++);
    // 两个单项式长度相等，并且相等数i==var的长度，才说明两个单项式相等
    return i == vars.size() && vars.size() == term.vars.size();
}
// 字典序排序，更小的放在前面
bool Term::operator< (const Term& term2) const {
    // term1 只有系数，放在后面，相当于字典序更大，返回false
    if (vars.size() == 0)
        return false;
    // term2 只有系数放后面，term1 放在前面，相当于字典序更小，返回true
    else if (term2.vars.size() == 0)
        return true;
    for (int i = 0; i < std::min(vars.size(), term2.vars.size()); i++)
        // 系数更小的排前面
        if (vars[i].id < term2.vars[i].id)
            return true;
        else if (term2.vars[i].id < vars[i].id)
            return false;
        // 指数更小的排前面
        else if (vars[i].exp < term2.vars[i].exp)
            return true;
        else if (term2.vars[i].exp < vars[i].exp)
            return false;
    // 长度更小的排前面
    return vars.size() - term2.vars.size() < 0;
}

// 要将Polynomial转为字符串显示在文本框中而不是打印在控制台，所以不是对<<进行重载
QString Polynomial::toString() const {
    QString result;
    int afterFirstTerm = 0;

    for (int j = 0; j < terms.size(); j++) {
        result.append(' ');
        // 根据输入流读取的不同变量标记，输出对应符号在控制台
        if (terms[j].coeff < 0)
            result.append('-');  // 在该单项式前加“-”号
        else if (afterFirstTerm) // result的第一个符号如果是+，就不要打印出来
            result.append('+');
        afterFirstTerm++;        // 之后出现的“+”号都要打印
        if (std::abs(terms[j].coeff) != 1) // 打印系数（如果绝对值不是1的话）
            result.append(' ').append(QString::number(std::abs(terms[j].coeff)));
        else if (terms[j].vars.size() == 0) // 该单项式只有系数1，没有变量和指数
            result.append(" 1");
        else
            result.append(' ');     // 有系数1，后面还有变量指数Variable，因此1可以省略
        // 打印向量Variable，如果指数是1则可以省略
        for (int i = 0; i < terms[j].vars.size(); i++) {
            // 先打印变量
            result.append(terms[j].vars[i].id);
            // 再打印指数
            if (terms[j].vars[i].exp != 1)
                result.append(QString::number(terms[j].vars[i].exp));
        }
    }

    result.append('\n');
    return result;
}

// 重载运算符+，对两个多项式进行相加
Polynomial Polynomial::operator+ (const Polynomial& polyn2) {
    Polynomial result;
    QVector<Term> tempTerms = terms + polyn2.terms; // 两个多项式/term列表
    // 复制当前多项式以及另一个多项式的所有项，到新的多项式中
    result.terms = tempTerms;
    bool erased = false;
    // 遍历新的多项式中的每一项，检查是否有相等的项，如果有则合并它们
    for (int i = 0; i < tempTerms.size()-1; ) {
        qDebug() << result.toString();
        qDebug() << i;
        qDebug() << tempTerms.size();;
        for (int j = i + 1; j < tempTerms.size(); j++) {
            qDebug() << "进来了没？ ";
            erased = false;
            // 如果两项相等（除了系数之外），则将它们的系数相加
            if (tempTerms[i] == tempTerms[j]) {

                tempTerms[i].coeff += tempTerms[j].coeff;
                qDebug() << tempTerms[i].coeff;
                // 删除多余的项；如果合并后的系数为零，则删除保留系数的项
                tempTerms.erase(tempTerms.begin() + j);
                qDebug() << tempTerms.size();
                // tempTerms.remove(j);
                if (tempTerms[i].coeff == 0)  // 系数为0没意义，也删了
                     tempTerms.erase(tempTerms.begin() + i);
                erased = true;
                break;
            }
            result.terms = tempTerms;
        }
        // 如果删除了任何节点，则重新从头开始处理
        if (erased == true){
            qDebug() << "回去 ";
            i = 0;
        }
        else i++;  // 没有与p1指向的单项式重复的元素，因此才可以++
    }
    result.terms = tempTerms;
    // 对新的多项式中的项进行排序
    std::sort(result.terms.begin(), result.terms.end()); // list容器内置的sort方法
    return result;
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->input1->setPlaceholderText("请输入以';'结尾的多项式");
    ui->input2->setPlaceholderText("请输入以';'结尾的多项式");
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_add_btn_clicked()
{
    ui->show->setPlainText("The result is:\n");
    Polynomial polyn1, polyn2;
    QString input1Text = ui->input1->text();
    QString input2Text = ui->input2->text();

    // 读取两个输入框的内容，转为流对象QTextStream
    QTextStream in1(&input1Text);
    QTextStream in2(&input2Text);

    // >>有重载，流对象可逐个单项式来进行读取
    in1 >> polyn1;
    in2 >> polyn2;

    Polynomial result = polyn1 + polyn2;

    // 转为字符串形式，显示到文本框中
    ui->show->setPlainText(result.toString());
}

void MainWindow::on_exit_btn_clicked()
{
    close();
}
